#include "KeywordProcessor.h"
#include "DirectoryScanner.h"

#include "cppjieba/Jieba.hpp"

#include <string>

using std::cerr;
using std::cout;
using std::endl;
using std::string;

/*

*/

// 构造函数
KeywordProcessor::KeywordProcessor()
{
    // 加载停用词
    load_stopword("./corpus/stopwords/en_stopwords.txt", m_enStopword);
    load_stopword("./corpus/stopwords/en_stopwords.txt", m_cnStopword);
};

// 接收语料库的目录路径，调用方法生成词典库和索引库
void KeywordProcessor::process(const std::string &chDir, const std::string &enDir)
{
    // 处理中文语料库
    create_cn_dirct(chDir, "./corpus/dict_cn.dat");
    // 处理英文语料库
    create_en_dirct(enDir, "./corpus/dict_en.dat");
    build_en_index(enDir, "./corpus/index_en.dat");
    create_cn_dirct(chDir, "./corpus/index_cn.dat");
}

// 加载停用词
void KeywordProcessor::load_stopword(const std::string &file, std::set<string> &stop_word)
{
    // 停用词加载到set容器
    std::ifstream ifs(file);
    if (!ifs)
    {
        cerr << "open stopword file failed" << endl;
        return;
    }
    // 按行读取单词，依次插入
    string word;
    while (getline(ifs, word))
    {
        // 每行读取一个单词，直接插入
        stop_word.insert(word);
    }
    ifs.close();
    // 关闭文件流，释放资源
};

// 判断是否为中文单词
bool KeywordProcessor::isChineseWord(const std::string &word)
{
    try
    {
        std::u32string u32str;
        utf8::utf8to32(word.begin(), word.end(), std::back_inserter(u32str));
        for (char32_t cp : u32str)
        {
            // 中文字符范围：U+4E00 到 U+9FFF
            if (cp < 0x4E00 || cp > 0x9FFF)
            {
                return false;
            }
        }
        return !u32str.empty();
    }
    catch (const utf8::exception &)
    {
        std::cerr << "UTF-8 解码错误: " << word << std::endl;
        return false;
    }
}

// 创建英文目录
void KeywordProcessor::create_en_dirct(const string &Dir, const string &outfile)
{
    // 处理英文语料库，生成词典库，统计每个单词出现的频率
    // map容器，单词和出现次数
    m_enDict.clear();

    // 调用scan方法，读取所有文件
    auto files = DirectoryScanner::scan(Dir);

    for (const auto &file : files)
    {
        std::ifstream ifs(file);
        // 依次打开每个文件，读取单词
        if (!ifs)
        {
            cerr << "open file failed: " << file << endl;
            // 文件无法打开，跳过
            continue;
        }
        string line;
        while (getline(ifs, line))
        {
            // 读取单词，并统计添加到m_enDict容器中
            // 先临时存储当前单词，进行判断，最好再添加
            string word;
            for (char c : line)
            {
                // 从line里依次遍历c，判断是否为字母
                if (isalpha(c))
                {
                    word += tolower(c);
                }
                else if (!word.empty())
                {
                    // 遇到非字母元素（空格，符号）且word不为空，则一个单词结束
                    // 在录入之前检查是否为停用词
                    if (m_enStopword.find(word) == m_enStopword.end())
                    {
                        // 非停用词，可以录入
                        m_enDict[word]++; // 该单词的int+1，用string来做key，int做value，value+1
                    }
                    word.clear(); // 继续下个单词
                }
            }
            // 行末单词后没有非字母元素，要单独处理
            if (!word.empty() && m_enStopword.find(word) == m_enStopword.end())
            {
                m_enDict[word]++;
            }
        }
        ifs.close();
        // 关闭文件流，释放资源
        std::copy(m_enDict.begin(), m_enDict.end(), std::back_inserter(m_vecenDict));
    }
    // 打开输出文件，写入词典库
    std::ofstream ofs(outfile);
    if (!ofs)
    {
        cerr << "open outfile failed" << endl;
        return;
    }
    for (const auto &[word, freq] : m_enDict)
    {
        ofs << word << "\t" << freq << "\n";
    }
    ofs.close();
    // 关闭文件流，释放资源
};

void KeywordProcessor::show_en_dict()
{
    for (const auto &[word, freq] : m_enDict)
    {
        cout << word << "\t" << freq << endl;
    }
}

void KeywordProcessor::show_en_index()
{
    for (const auto &elem : m_enindex)
    {
        cout << elem.first;
        const std::set<int> s = elem.second;
        for (auto &idx : s)
        {
            cout << "\t" << idx;
        }
        cout << endl;
    }
}

void KeywordProcessor::build_en_index(const string &dict, const string &index)
{
    // 读取词典库，构建索引
    for (int i = 0; i < m_enDict.size(); i++)
    {
        // 取走单词
        string word = m_vecenDict[i].first;
        for (int j = 0; j < word.size(); j++)
        {
            string ch(1, word[j]); // 初始化
            // ch号key位置，插入单词的索引号i
            m_enindex[ch].insert(i);
        }
    }


    std::ofstream ofs(index);

    for (const auto &elem : m_enindex)
    {
        ofs << elem.first;
        const std::set<int> &s = elem.second;
        for (auto &idx : s)
        {
            ofs << "\t" << idx;
        }
        ofs << "\n";
    }
    ofs.close();
};

void KeywordProcessor::create_cn_dirct(const string &Dir, const string &outfile)
{

    auto files = DirectoryScanner::scan(Dir);

    for (const auto &file : files)
    {
        std::ifstream ifs(file);
        if (!ifs)
        {
            continue;
        }

        //
        string content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
        ifs.close();

        std::vector<string> words;
        m_tokenizer.Cut(content, words);
        for (const auto &word : words)
        {
            if (m_cnStopword.find(word) == m_cnStopword.end() && isChineseWord(word))
            {
                m_cnDict[word]++;
            }
        }
    }
    std::copy(m_enDict.begin(), m_enDict.end(), std::back_inserter(m_vecenDict));

    std::ofstream ofs(outfile);
    if (!ofs)
    {
        cerr << "open outfile failed" << endl;
        return;
    }
    for (const auto &[word, freq] : m_cnDict)
    {
        ofs << word << "\t" << freq << "\n";
    }
    ofs.close();
}

void KeywordProcessor::build_cn_index(const string &dict, const string &index)
{
    // 读取词典库，构建索引
    for (int i = 0; i < m_cnDict.size(); i++)
    {
        // 取走单词
        string word = m_veccnDict[i].first;
        const char *it = word.c_str();
        const char *end = word.c_str() + word.size();

        while (it != end)
        {
            auto start = it;
            utf8::next(it, end);

            // 提取单个字符串
            string ch(start, it);
            m_cnindex[ch].insert(i);
        }
    }

    std::ofstream ofs(index);
    if (!ofs)
    {
        cerr << "ofs failed" << endl;
        return;
    }
    for (const auto &elem : m_cnindex)
    {
        ofs << elem.first;
        const std::set<int> &s = elem.second;
        for (auto &idx : s)
        {
            ofs << "\t" << idx;
        }
        ofs << "\n";
    }
    ofs.close();
}